package rjeutsky.xml.stax;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stax.StAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.SAXException;

import rjeutsky.models.CardThema;
import rjeutsky.models.CardType;
import rjeutsky.models.OldCard;
import rjeutsky.models.TypeEnum;
import rjeutsky.models.builders.ConcreteOldCardBuilder;
import rjeutsky.xml.common.Constants;
import rjeutsky.xml.common.OldCardFileWriter;
import rjeutsky.xml.common.TagNames;

public class OldCardStaxSerializer extends OldCardFileWriter {

	@Override
	public boolean deserializeFromFile() {
		try {
			File file = new File(filePath);
			XMLStreamReader node = XMLInputFactory.newInstance()
				.createXMLStreamReader(new FileReader(file));
			
			SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
	        Schema schema = factory.newSchema(new File(Constants.SCHEMA_PATH));

	        Validator validator = schema.newValidator();
	        validator.validate(new StAXSource(node));
	        
	        // validation passed. read once more to parse
	        node.close();
	        
	        node = XMLInputFactory.newInstance()
				.createXMLStreamReader(new FileReader(file));
			
			node.nextTag();
			parse(node);
			node.close();
			return true;
		} catch (XMLStreamException e) {
			System.err.println(e.getMessage());
		} catch (FileNotFoundException e) {
			System.err.println(e.getMessage());
		} catch (IOException e) {
			System.err.println(e.getMessage());
		} catch (SAXException e) {
			System.err.println(e.getMessage());
		}
		
		return false;
	}

	@Override
	public String getInfo() {
		return "Writes with XMLStreamWriter, reads with STAX parser";
	}

	@Override
	public List<OldCard> getOldCardList() {
		return cards;
	}
	
	protected void parse(XMLStreamReader node) throws XMLStreamException{
		cards = new LinkedList<OldCard>();
		while (true) {
			node.nextTag();
			if (node.isStartElement()) {
				cards.add(parseOldCard(node));
			} else {
				return;
			}
		}
	}
	
	protected OldCard parseOldCard (XMLStreamReader node) throws XMLStreamException {
		ConcreteOldCardBuilder builder = new ConcreteOldCardBuilder();
		while (true) {
			node.nextTag();
			String tagName = node.getName().toString(); 
			if (tagName.equals(TagNames.THEMA)) {
				builder.setThema(CardThema.valueOf(parseTextNode(node)));
			} else if (tagName.equals(TagNames.COUNTRY)) {
				builder.setCountry(parseTextNode(node));
			} else if (tagName.equals(TagNames.YEAR)) {
				builder.setYear(Integer.parseInt(parseTextNode(node)));
			} else if (node.isStartElement() && tagName.equals(TagNames.CARD_TYPE)) {
				builder.setType(parseType(node));
			} else if (tagName.equals(TagNames.AUTHOR)) {
				builder.setAuthor(parseTextNode(node));
			} else if (tagName.equals(TagNames.VALUABLE)) {
				builder.setValuable(parseTextNode(node));
			} else {
				builder.createNewCard();
				builder.buildAll();
				return builder.getCard();
			}
		}
	}
	
	protected CardType parseType(XMLStreamReader node) throws XMLStreamException {
		CardType type = new CardType();
		type.setIsSent(Boolean.parseBoolean(node.getAttributeValue(null, TagNames.IS_SEND)));
		node.next();
		type.setType(TypeEnum.valueOf(node.getText()));
		node.nextTag();
		return type;
	}
	
	protected String parseTextNode(XMLStreamReader node) throws XMLStreamException{
		node.next();
		String text = node.getText();
		node.nextTag();
		return text;
	}
}
